package com.njupt.simulation.utils;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 泊松分布类，用于在指定圆形区域内根据泊松分布生成随机点
 */
public class Poisson {

    // 圆心的经纬度坐标（单位：度）
    private double centerLat;
    private double centerLon;

    // 圆的半径（单位：公里）
    private double radius;

    // 在圆形区域内随机撒点的密度（单位：点数/平方公里）
    private double density;

    // 随机数生成器
    private Random random;

    /**
     * 构造函数，初始化泊松分布类的参数
     * @param centerLat 圆心的纬度
     * @param centerLon 圆心的经度
     * @param radius 圆的半径（单位：公里）
     * @param density 在圆形区域内随机撒点的密度（单位：点数/平方公里）
     */
    public Poisson(double centerLat, double centerLon, double radius, double density) {
        this.centerLat = centerLat;
        this.centerLon = centerLon;
        this.radius = radius;
        this.density = density;
        this.random = new Random();
    }

    /**
     * 根据泊松分布生成随机点数
     * @param lambda 泊松分布的期望值
     * @return 生成的随机点数
     */
    private int poissonRandom(double lambda) {
        int k = 0;
        double p = 1.0;
        double L = Math.exp(-lambda);
        do {
            k++;
            p *= random.nextDouble();
        } while (p > L);
        return k - 1;
    }

    /**
     * 在圆形区域内生成均匀分布的随机点
     * @return 随机点的列表，每个点用经纬度表示
     */
    public List<double[]> generateRandomPoints() {
        List<double[]> points = new ArrayList<>();
        double radiusMeters = radius * 1000;
        double areaMeters2 = Math.PI * radiusMeters * radiusMeters;
        // 期望的点的数量（泊松分布的λ参数）
        double lambda = density * (areaMeters2 / 1_000_000); // 将平方米转换为平方公里
        int numPoints = poissonRandom(lambda);

        for (int i = 0; i < lambda; i++) {
            double r = radius * Math.sqrt(random.nextDouble());
            double theta = 2 * Math.PI * random.nextDouble();

            double x = r * Math.cos(theta) * 1000; // 转换为米
            double y = r * Math.sin(theta) * 1000; // 转换为米

            // 将平面坐标转换为经纬度坐标
            double[] latLon = xyToLatLon(x, y);
            points.add(latLon);
        }

        return points;
    }

    /**
     * 将平面坐标（单位：米）转换为经纬度坐标
     * @param x 平面坐标x轴偏移量
     * @param y 平面坐标y轴偏移量
     * @return 经纬度坐标，第一个元素为纬度，第二个元素为经度
     */
    private double[] xyToLatLon(double x, double y) {
        double earthRadius = 6378137; // 地球半径（单位：米）

        double dLat = y / earthRadius;
        double dLon = x / (earthRadius * Math.cos(Math.toRadians(centerLat)));

        double lat = centerLat + Math.toDegrees(dLat);
        double lon = centerLon + Math.toDegrees(dLon);

        return new double[]{lon, lat,0.0};
    }

    /**
     * 显示生成的随机点的经纬度坐标
     */
    public void displayPoints() {
        List<double[]> points = generateRandomPoints();

        for (double[] point : points) {
            System.out.println("Latitude: " + point[0] + ", Longitude: " + point[1]);
        }
        System.out.println("生成的随机点个数: " + points.size()); // 打印点数个数
    }

    public static void main(String[] args) {
        double centerLat = 29; // 圆心的纬度
        double centerLon = 49; // 圆心的经度
        double radius = 100; // 半径（单位：公里）
        double density = 10; // 密度（单位：点数/平方公里）

        Poisson poissonPoints = new Poisson(centerLat, centerLon, radius, density);
        poissonPoints.displayPoints();
    }
}

